package mtask

import (
	"errors"
	"fmt"
	"gitee.com/dennis-mxx/mxx-core/mlogger"
	"github.com/robfig/cron/v3"
	"reflect"
	"runtime"
)

type Entry struct {
	Name    string
	EntryID cron.EntryID
	Cron    string
	Func    func()
}
type task struct {
	wait  chan bool
	Cron  *cron.Cron
	Entry map[string]*Entry
}

func NewDefault() *task {
	return &task{
		Cron:  cron.New(cron.WithSeconds()),
		Entry: map[string]*Entry{},
	}
}

func New(cron *cron.Cron) *task {
	return &task{
		Cron:  cron,
		Entry: map[string]*Entry{},
	}
}
func (domain *task) AddJob(name string, cronStr string, cmd cron.Job) (cron.EntryID, error) {
	if domain.Entry[name] != nil {
		return 0, errors.New(name + " Job Exists")
	}
	execFun := cron.FuncJob(func() {
		defer func() {
			if err := recover(); err != nil {
				mlogger.Logger.Error(fmt.Sprintf("task[%s] run failure", name), err)
			}
		}()
		mlogger.Logger.Info(fmt.Sprintf("task[%s] run start by cron : %s", name, cronStr))
		cmd.Run()
		mlogger.Logger.Info(fmt.Sprintf("task[%s] run end", name))
	})
	entryID, err := domain.Cron.AddJob(cronStr, execFun)

	if err != nil {
		mlogger.Logger.Error(fmt.Sprintf("task[%s] addFunc failure by cron %s : %s", name, cronStr, err.Error()))
		return 0, err
	} else {
		domain.Entry[name] = &Entry{
			Name:    name,
			EntryID: entryID,
			Cron:    cronStr,
			Func:    execFun,
		}
	}
	return 0, err
}
func (domain *task) AddFunc(name string, cronStr string, fun func()) (cron.EntryID, error) {
	if domain.Entry[name] != nil {
		return 0, errors.New(name + " Job Exists")
	}
	execFun := cron.FuncJob(func() {
		defer func() {
			if err := recover(); err != nil {
				mlogger.Logger.Error(fmt.Sprintf("task[%s] run failure", name), err)
			}
		}()
		mlogger.Logger.Info(fmt.Sprintf("task[%s] run start by cron : %s", name, cronStr))
		fun()
		mlogger.Logger.Info(fmt.Sprintf("task[%s] run end", name))
	})
	entryID, err := domain.Cron.AddJob(cronStr, execFun)

	if err != nil {
		mlogger.Logger.Error(fmt.Sprintf("task[%s] addFunc failure by cron %s : %s", name, cronStr, err.Error()))
		return 0, err
	} else {
		domain.Entry[name] = &Entry{
			Name:    name,
			EntryID: entryID,
			Cron:    cronStr,
			Func:    execFun,
		}
	}
	return 0, err
}

func (domain *task) StartAndWait() {
	domain.Cron.Start()
	<-domain.wait
}

func (domain *task) Start() {
	domain.Cron.Start()
}

func (domain *task) CloseAwait() {
	domain.Cron.Stop()
	domain.wait <- true
}

func (domain *task) Close() {
	domain.Cron.Stop()
	domain.wait <- true
}
func (domain *task) Remove(name string) error {
	entry := domain.Entry[name]
	if entry == nil {
		return errors.New(name + " Entry Not Found")
	}
	domain.Cron.Remove(entry.EntryID)
	return nil
}
func (domain *task) RemoveById(entryID cron.EntryID) error {
	domain.Cron.Remove(entryID)
	return nil
}

func (domain *task) Print() {
	for _, object := range domain.Entry {
		funcName := runtime.FuncForPC(reflect.ValueOf(object.Func).Pointer()).Name()
		mlogger.Logger.Info(fmt.Sprintf("{ID:%v,name:%s,cron:%s,func:%s}", object.EntryID, object.Name, object.Cron, funcName))
	}
}
